//
// Created by tan on 22-8-20.
//

#include <iomanip>
#include "DataCtrl.h"


bool DataCtrl::pointsCmp(GPoint &p1, GPoint &p2) {
    /**
     * compare points by x, y coords
     * @param: p1 a point
     * @param: p2 another point
     * @return: return a true or false
     * **/
    if (p1.xCoord > p2.xCoord) {
        return false;
    } else if (p1.xCoord == p2.xCoord) {
        if (p1.yCoord >= p2.yCoord) {
            return true;
        } else {
            return false;
        }
    } else {
        return true;
    }
}

void DataCtrl::genPointNo() {
    /**
     * To generate new numbers of points
     * **/
    sort(allPoints.begin(), allPoints.end(), this->pointsCmp);

    GPoint *cur;
    GPoint *last;

    cur = last = &allPoints[0];
    cur->pointNo = last->pointNo = 0;

    set<int> curPointNos;
    curPointNos.insert(allPoints[0].lineNo);

    lineNoToPointNoMap[allPoints[0].lineNo].insert(1);
    string curKey = to_string(cur->xCoord) + to_string(cur->yCoord);
    xyToPointNoMap[curKey] = cur->pointNo;

    //    cout << curKey << endl;

    for (int i = 1; i < allPoints.size(); ++i) {
        cur = &allPoints[i];
        if (cur->xCoord == last->xCoord and cur->yCoord == last->yCoord) {
            curPointNos.insert(cur->lineNo);
            cur->pointNo = last->pointNo;
        } else {
            pointNoToLineNoMap.emplace_back(curPointNos);
            cur->pointNo = last->pointNo + 1;
            curPointNos.clear();
            curPointNos.insert(cur->lineNo);

            curKey = to_string(cur->xCoord) + to_string(cur->yCoord);

            xyToPointNoMap[curKey] = cur->pointNo;
        }

        lineNoToPointNoMap[cur->lineNo].insert(cur->pointNo);

        last = cur;
    }

    lineNoToPointNoMap[cur->lineNo].insert(cur->pointNo);
    curKey = to_string(cur->xCoord) + to_string(cur->yCoord);
    xyToPointNoMap[curKey] = cur->pointNo;
}

set<int> DataCtrl::getLineNosByPointNo(int pointNo) {
    /**
     * Get lineNo by pointNo
     * @param: point No
     * @return: Line numbers
     * **/
    return pointNoToLineNoMap[pointNo];
}

set<int> DataCtrl::getPointNosByLineNo(int lineNo) {
    /**
     * Get points No by Line No
     * @param: Line No
     * @return: Point No's
     * **/
    set<int> res;
    if (this->lineNoToPointNoMap.find(lineNo) != this->lineNoToPointNoMap.end()) {
        res = this->lineNoToPointNoMap.find(lineNo)->second;
    }
    return res;
}

int DataCtrl::getPointNoByXY(double x, double y) {
    /**
     * Get point No by (x, y)
     * @param: x, x coord of the point
     * @param: y, y coord of the point
     * @return: The point No
     * **/
    string key = to_string(precision(x)) + to_string(precision(y));
    int res = -1;
    if (this->xyToPointNoMap.find(key) != this->xyToPointNoMap.end()) {
        res = this->xyToPointNoMap.find(key)->second;
    }
    return res;
}


DataCtrl DataCtrl::readLines(const string &file) {
    /**
     * Read all lines from csv file
     * @param: csv file of the lines
     * @return: Instance of DataCtrl
     * **/
    Document document(file);
    vector<string> strOrgGerber = document.GetColumn<string>("strOrgGerber");
    vector<string> lineNo = document.GetColumn<string>("lineNo");
    vector<string> coords = document.GetColumn<string>("coords");
    vector<string> blockNo = document.GetColumn<string>("blockNo");
    vector<string> strNewGerber = document.GetColumn<string>("strNewGerber");
    vector<string> netCoords = document.GetColumn<string>("netCoords");
    vector<string> originalCoords = document.GetColumn<string>("originalCoords");
    vector<string> action = document.GetColumn<string>("action");
    vector<string> linesCoords = document.GetColumn<string>("linesCoords");
    vector<string> lineR = document.GetColumn<string>("line_r");
    int lineSize;
    lineSize = (int) document.GetRowCount();
    for (int i = 0; i < lineSize; ++i) {
        GRow gRow(
                strOrgGerber[i],
                lineNo[i],
                coords[i],
                blockNo[i],
                strNewGerber[i],
                netCoords[i],
                originalCoords[i],
                action[i],
                lineR[i]
        );
        vector<double> allCoords = gRow.coords;
        allCoords.insert(allCoords.end(), gRow.originalCoords.begin(), gRow.originalCoords.end());
        for (int j = 0; j < allCoords.size(); j = j + 2) {
            GPoint gPoint(gRow.lineNo, allCoords[j], allCoords[j + 1], gRow.action);
            this->allPoints.emplace_back(gPoint);
        }
        lineNoToBlockNoMap[gRow.lineNo] = gRow.blockNo;
        blockNoToLineNoMap[gRow.blockNo].insert(gRow.lineNo);
        allRows.emplace_back(gRow);
    }
    this->genPointNo();
    return *this;
}

int DataCtrl::getBlockNoByLineNo(int lineNo) {
    /**
     * Get block No by line No
     * @param: lineNo line No
     * @return: return the block No
     * **/
    int blockNo = -1;
    if (this->lineNoToBlockNoMap.find(lineNo) != this->lineNoToBlockNoMap.end()) {
        blockNo = this->lineNoToBlockNoMap.find(lineNo)->second;
    }
    return blockNo;
}

set<int> DataCtrl::getLineNosByBlockNo(int blockNo) {
    /**
     * Get line No's by block No
     * @param: blockNo block No
     * @return: The line No's
     * **/
    set<int> lineNos;

    if (this->blockNoToLineNoMap.find(blockNo) != this->blockNoToLineNoMap.end()) {
        lineNos = this->blockNoToLineNoMap.find(blockNo)->second;
    }

    return lineNos;
}

void DataCtrl::genDataObj() {
    for (const auto &row: allRows) {
        auto _lineNo = row.lineNo;
        auto _blockNo = row.blockNo;
        auto _action = row.action;
        string _lineR = row.lineR;
        vector<int> _coords;
        vector<int> _originalCoords;
        vector<int> _linesCoords;

        int tmp;
        for (int i = 0; i < row.coords.size(); i = i + 2) {
            double _x = row.coords[i];
            double _y = row.coords[i + 1];
            tmp = this->getPointNoByXY(_x, _y);
            _coords.emplace_back(tmp);
        }

        for (int i = 0; i < row.originalCoords.size(); i = i + 2) {
            double _x = row.originalCoords[i];
            double _y = row.originalCoords[i + 1];
            tmp = this->getPointNoByXY(_x, _y);
            _originalCoords.emplace_back(tmp);
        }

        if (row.action == "G01C" || row.action == "G01C" || row.action == "G03R" || row.action == "G03C" ||
            row.action == "G03O") {
            GLine gLine(_lineNo, _coords, _blockNo, _originalCoords, _action, _lineR);
            this->allLines.emplace_back(gLine);
        } else if (row.action == "G04") {
            GPad gPad(_lineNo, _coords, _blockNo, _originalCoords, _action);
            this->allPads.emplace_back(gPad);
        } else if (row.action == "G36") {
            GSurface gSurface(_lineNo, _coords, _blockNo, _originalCoords, _action);
            this->allSurfaces.emplace_back(gSurface);
        }
    }
}

vector<GPoint> DataCtrl::pointsBetween(double x1, double y1, double x2, double y2) {
    vector<GPoint> res;
    vector<GPoint> BPoints;
    x1 = precision(x1);
    y1 = precision(y1);
    x2 = precision(x2);
    y2 = precision(y2);
    double xMin = precision(min(x1, x2));
    double xMax = precision(max(x1, x2));

    double yMin = precision(min(y1, y2));
    double yMax = precision(max(y1, y2));

    for (const auto &tmp: this->allPoints) {
        if (tmp.xCoord >= xMin and tmp.xCoord <= xMax) {
            BPoints.emplace_back(tmp);
        } else {
            continue;
        }
    }
    for (const auto &item: BPoints) {
        if (item.yCoord >= yMin and item.yCoord <= yMax) {
            res.emplace_back(item);
        }
    }

    return res;
}






